package com.example.utils.util.StreamUtils;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @TODO Stream 将要处理的元素集合看作一种流，在流的过程中，借助Stream API对流中的元素进行操作，比如：筛选、排序、聚合等、
 * Stream 可以有数组或者集合创建，对流的操作分为两种
 * 1. 中间操作，每次返回一个新的流，可以有多个
 * 2. 终端操作，每个流只能进行一次终端操作，终端操作结束后流无法再次使用。终端会产生一个新的集合或值。
 * Stream几个特性：
 * 1. stream 不存储防数据，而是按照特定的规则对数据进行计算，一般会输出结果
 * 2. stream 不会改变数据源，通常情况下会产生一个新的集合或值
 * 3. stream 具有延迟执行特性，只有调用终端操作时，中间操作才会执行
 * Optional:
 * Optional类是一个可以为null的容器对象，如果存在则isPresent()方法会返回true,调用get()方法会返回该对象
 * @ClassName StreamStart
 * @Author 81373
 * @Date 2022/10/2 18:39
 */
public class StreamStart {

    public static void main(String[] args) {
        List<Integer> integers = Arrays.asList(7, 4, 6, 8, 2, 9);
        List<Integer> integers1 = Arrays.asList(2, 5, 7, 1, 9);
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("Tom", 8900, 23, "male", "BeiJing"));
        personList.add(new Person("Jack", 7000, 21, "male", "ShangHai"));
        personList.add(new Person("Lily", 7800, 25, "female", "ZhengZhou"));
        personList.add(new Person("Anni", 8200, 23, "male", "HangZhou"));
        personList.add(new Person("LiLei", 8900, 24, "male", "BeiJing"));
        personList.add(new Person("Alisa", 9500, 30, "male", "ShenZhen"));
        //region stream 的创建
        // 1. 通过 java.util.Collection.stream() 方法用集合创建流
        List<String> list = Arrays.asList("a", "b", "c");
        // 创建一个顺序流
        Stream<String> stream = list.stream();
        // 创建要给并行流
        Stream<String> parallelStream = list.parallelStream();
        // 通过 parallel() 把顺序流转换成并行流
        Optional<String> findStream = stream.parallel().filter(x -> x.equals("a")).findFirst();
        // 2.  使用java.Arrays.stream(T[] array)方法创建数组流
        int[] array = {1, 3, 5, 8, 9};
        IntStream arrayStream = Arrays.stream(array);
        // 3. 使用Stream 的静态方法： of()、 iterate()、 generate()
        Stream<Integer> streamOf = Stream.of(1, 4, 6, 8);
        Stream<Integer> streamIterate = Stream.iterate(0, (x) -> x + 3).limit(4);
        streamIterate.forEach(System.out::println);
        Stream<Double> streamGenerate = Stream.generate(Math::random).limit(3);
        streamGenerate.forEach(System.out::println);
        // endregion

        // region 遍历/匹配

        // 遍历输出符合条件的元素
        integers.stream().filter(x -> x > 6).forEach(System.out::println);
        // 匹配第一个
        Optional<Integer> first = integers.stream().filter(x -> x > 6).findFirst();
        // 匹配任意 （适用于并行流）
        Optional<Integer> any = integers.parallelStream().filter(x -> x > 6).findAny();
        // 是否包含符合特定条件的元素
        boolean b = integers.stream().anyMatch(x -> x > 6);
        System.out.println("匹配第一个 = " + first);
        System.out.println("匹配任意 = " + any);
        System.out.println("是否包含符合特定条件的元素 = " + b);
        // endregion

        //region 筛选
        // 1. 筛选出 结果中大于7的值
        integers1.stream().filter(x -> x > 7).forEach(System.out::println);
        // 2. 筛选 工资高于8000 的人形成新的集合。形成新的结合依赖 collect(收集)
        List<String> collect = personList.stream().filter(x -> x.getSalary() > 8000).map(Person::getName)
                .collect(Collectors.toList());
        System.out.println("工资高于8000 的员工姓名 = " + collect);
        // endregion

        // region  聚合 (min/max/count)
        // 获取String集合中最长的元素
        List<String> strings = Arrays.asList("", "ppt", "tesdg", "dsgasger");
        Optional<String> max = strings.stream().max(Comparator.comparing(String::length));
        System.out.println("String集合中最长的元素 = " + max);
        // 获取 int集合中的最大值
        // 自然排序
        Optional<Integer> max1 = integers.stream().max(Integer::compareTo);
        // 自然定义排序
        Optional<Integer> max2 = integers.stream().max(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1.compareTo(o2);
            }
        });
        System.out.println("自然排序的最大值 = " + max1);
        System.out.println("自然定义排序的最大值 = " + max2);
        // 获取员工工资最高的人
        Optional<Person> max3 = personList.stream().max(Comparator.comparing(Person::getSalary));
        System.out.println("获取员工工资最高的人 = " + max3);
        // 计算Integer集合中大于6的元素的个数
        long count = integers.stream().filter(x -> x > 6).count();
        System.out.println("集合中大于6的元素的个数 = " + count);
        // endregion

        //region 映射（map/flatMap）
        // 映射，可以将一个流的元素按照一定的映射规则，映射到另一个流中，分为map和flatMap
        // map：     接收一个函数作为参数，该函数会被应用到每个元素上，并将其映射成一个新的元素
        // flatMap： 接受一个函数作为参数，将流中的每个值都换成另一个流，然后把所有流连接成一个流
        // 1. 英文字符串数组的元素全部改为大写，
        String[] strArr = {"abcd","iohjuh","defde","fTr"};
        List<String> collect5 = Arrays.stream(strArr).map(String::toUpperCase).collect(Collectors.toList());
        // 整数数组每个元素加3
        List<Integer> collect6 = integers1.stream().map(m -> m + 3).collect(Collectors.toList());
        // 员工薪资全部增加1000
        // 不改变原来员工集合的方式
        List<Person> collect7 = personList.stream().map(person -> {
            Person personNew = new Person(person.getName(), 0, 0, null, null);
            personNew.setSalary(person.getSalary() + 1000);
            return personNew;
        }).collect(Collectors.toList());
        // 改变原来员工集合的方式
        List<Person> collect8 = personList.stream().map(person -> {
            person.setSalary(person.getSalary() + 1000);
            return person;
        }).collect(Collectors.toList());
        // 将两个字符数组合并成一个新的字符数组
        List<String> strings2 = Arrays.asList("a,k,l,y", "z,g,t,r");
        List<String> collect9 = strings2.stream().flatMap(s -> {
            // 将每个元素转成一个stream
            String[] split = s.split(",");
            Stream<String> stream1 = Arrays.stream(split);
            return stream1;
        }).collect(Collectors.toList());
        // endregion

        // region 归约(reduce)
        // 归约，顾名思义，是把一个流缩减成一个值，能实现对集合的求和、求乘积和求最值操作
        // 1. 求 Integer 集合的元素之和、乘积和最大值
        // 求和方式 1
        Optional<Integer> reduce = integers.stream().reduce((x, y) -> x + y);
        // 求和方式 2
        Optional<Integer> reduce1 = integers.stream().reduce(Integer::sum);
        // 求和方式 3
        Integer reduce2 = integers.stream().reduce(0, Integer::sum);
        // 求乘积
        Optional<Integer> reduce3 = integers.stream().reduce((x, y) -> x * y);
        // 求最大值方式 1
        Optional<Integer> reduce4 = integers.stream().reduce((x, y) -> x > y ? x : y);
        // 求最大值 2
        Integer reduce5 = integers.stream().reduce(1, Integer::max);
        // 2. 求所有员工的工资之和和最高工资
        // 求工资之和方式 1
        Optional<Integer> reduce6 = personList.stream().map(Person::getSalary).reduce(Integer::sum);
        // 求工资之和方式 2
        Integer reduce7 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(), (sum1, sum2) -> sum1 + sum2);
        // 求工资之和方式 3
        Integer reduce8 = personList.stream().reduce(0, (sum, p) -> sum += p.getSalary(), Integer::sum);
        // 求最高公司方式 1
        //Integer reduce9 = personList.stream().reduce(0, (max, p) -> max > p.getSalary() ? max : p.getSalary(), Integer::max);
        // 求最高工资方式 2
        //Integer reduce10 = personList.stream().reduce(0, (max, p) -> max > p.getSalary() ? max : p.getSalary(), (max1, max2) -> max1 > max2 ? max1 : max2);
        // endregion

        // region 收集(collect)
        // 把一个流收集起来，最终可以是收集成一个值，也可以是一个新的集合
        // 1. 归集(toList/toSet/toMap)
        Map<String, Person> collect10 = personList.stream().filter(p -> p.getSalary() > 8000).collect(Collectors.toMap(Person::getName, p -> p));
        // 2. 统计(count/averaging)
        // 求总数
        Long collect11 = personList.stream().collect(Collectors.counting());
        // 求平均工资
        Double collect12 = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
        // 求最高工资
        Optional<Integer> collect13 = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compareTo));
        // 求粽子之和
        Integer collect14 = personList.stream().collect(Collectors.summingInt(Person::getSalary));
        // 一次性统计所有信息
        DoubleSummaryStatistics collect15 = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));
        // 3. 分组(partitonging/groupingBy)
        // 分区： 将stream 按条件分为两个Map，比如员工按照薪资是否高于8000分为两部分
        // 分组： 将集合分为多个Map，比如员工按照性别分组，有单级分组和多级分组
        // 将员工按照薪资是否高于800分组
        Map<Boolean, List<Person>> collect16 = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
        // 将员工按照性别分组
        Map<String, List<Person>> collect17 = personList.stream().collect(Collectors.groupingBy(Person::getSex));
        // 将员工先按照性别分组，再按照地区分组
        Map<String, Map<String, List<Person>>> collect18 = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
        // 4. 接合(joining)
        // joining 可以将stream 中的元素用特定的连接符（没有的话，则直接连）链接成一个新的字符串
        String collect19 = personList.stream().map(p -> p.getName()).collect(Collectors.joining(","));
        List<String> strings3 = Arrays.asList("A", "B", "C");
        String collect20 = strings3.stream().collect(Collectors.joining("-"));
        // 归约(reducing)
        // 每个员工减去起征点之后的薪资总和（这个例子并不严谨，但一时没有想到好的例子）
        Integer collect21 = personList.stream().collect(Collectors.reducing(0, Person::getSalary, (i, j) -> (i + j) - 5000));
        // endregion

        // region 外边
        // sorted, 中间操作，有两种排序
        // sorted(): 自然排序， 流中元素需实现Comparable接口
        // sorted(Comparator): Comparator 排序器自定义排序
        // 按照工资升序排序(自然排序)
        List<String> collect22 = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName).collect(Collectors.toList());
        // 按照工资倒叙排序
        List<String> collect23 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed()).map(Person::getName).collect(Collectors.toList());
        // 先按照工资排序 再按年龄升序排序
        List<String> collect24 = personList.stream().sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName).collect(Collectors.toList());
        // 先按工资再按年龄自定义排序（降序）
        List<String> collect25 = personList.stream().sorted((p1, p2) -> {
            if (p1.getSalary() == p2.getSalary()) {
                return p2.getAge() - p1.getAge();
            } else {
                return p2.getSalary() - p1.getSalary();
            }
        }).map(Person::getName).collect(Collectors.toList());
        // endregion

        String[] arr1 = {"a", "b", "c", "d"};
        String[] arr2 = {"d", "e", "f", "g"};
        // region 提取/组合
        // 流也可以进行合并、去重、限制、跳过等操作
        Stream<String> arr11 = Stream.of(arr1);
        Stream<String> arr21 = Stream.of(arr2);
        // concat ： 合并两个流 distinct： 去重
        List<String> collect26 = Stream.concat(arr11, arr21).distinct().collect(Collectors.toList());
        // limit :  限制从流中获得前 n 个数据
        List<Integer> collect27 = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
        // skip : 跳过前n个数据
        List<Integer> collect28 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());
        // endregion


        // region   list 去空 ，去重, 求平均值  String 转 List， 统计集合元素重复次数
        List<String> list1 = new ArrayList();
        list1.add(null);
        list1.add("");
        list1.add("");
        list1.add(null);
        list1.add("aaa");
        list1.add("aaa");
        list1.add("bbb");
        // 第一种
        list1.removeAll(Collections.singleton(""));
        list1.removeAll(Collections.singleton(null));
        // 第二种
        List<String> list2 = new ArrayList();
        // 去重
        List<String> collect4 = list1.stream().distinct().collect(Collectors.toList());
        // 去空 并用 set 去重
        list2.addAll(
                list1.stream().map(m -> {
                    if (m == null || "".equals(m)) {
                        return null;
                    }
                    return m;
                }).filter(Objects::nonNull).collect(Collectors.toSet()));
        // list 平均值
        Double collect1 = integers.stream().collect(Collectors.averagingInt(x -> x));

        String a = "1,2,3";
        // 转为 List<Long>
        List<Long> collect2 = Arrays.stream(a.split(",")).map(m -> Long.parseLong(m.trim())).collect(Collectors.toList());
        // 转为 List<String>
        List<String> strings1 = Arrays.asList(a.split(","));
        // List 转成 逗号分隔的String 字符串
        String collect3 = strings1.stream().map(String::valueOf).collect(Collectors.joining(","));

        // 统计集合元素重复次数1
        Map<String, Long> collect29 = list1.stream().collect(Collectors.groupingBy(p -> p, Collectors.counting()));
        // 统计集合元素重复次数2


        // endregion


    }
}
